package com.tax.config.redisConfig.rediscache;

import com.tax.config.redisConfig.rediscondition.RedisObjectCondition;
import com.tax.config.redisConfig.rediscondition.RedisStringCondition;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
 * Redis缓存配置
 * 主要做自定义：Key和Value改默认的JDK序列化方案为String
 *
 */
@Configuration
@EnableCaching
public class RedisConfigForCache extends CachingConfigurerSupport {

	@Bean
	public CacheManager cacheManager(RedisConnectionFactory factory) {
		RedisCacheManager rcm = RedisCacheManager.builder(factory).cacheDefaults(defaultCacheConfig(10000))
				.transactionAware().build();
		return rcm;
	}

	/**
	 * @Description: 当配置项string时序列化模板类型为String
	 **/
	@Conditional(RedisStringCondition.class)
	@Bean("redisTemplate")
	public RedisTemplate<String, String> redisTemplateString(RedisConnectionFactory factory) {
		StringRedisTemplate redisTemplate = new StringRedisTemplate(factory);
		StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
		redisTemplate.setKeySerializer(stringRedisSerializer);
		redisTemplate.setHashKeySerializer(stringRedisSerializer);
		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}

	/**
	 * @Description: 当配置项string时序列化模板类型为Object,此时使用Jackson2JsonRedisSerializer进行序列化value值
	 **/
	@Conditional(RedisObjectCondition.class)
	@Bean("redisTemplate")
	public RedisTemplate<String, Object> redisTemplateObject(RedisConnectionFactory factory) {
		RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
		// 配置连接工厂
		template.setConnectionFactory(factory);
		//设置序列化工具
		setSerializer(template);
		template.afterPropertiesSet();
		return template;
	}

	private void setSerializer(RedisTemplate<String, Object> template) {
		//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值（默认使用JDK的序列化方式）
		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

		ObjectMapper om = new ObjectMapper();
		// 指定要序列化的域，field,get和set,以及修饰符范围，ANY是都有包括private和public
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		// 指定序列化输入的类型，类必须是非final修饰的，final修饰的类，比如String,Integer等会跑出异常
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		jackson2JsonRedisSerializer.setObjectMapper(om);

		// 值采用json序列化
		template.setValueSerializer(jackson2JsonRedisSerializer);
		//使用StringRedisSerializer来序列化和反序列化redis的key值
		template.setKeySerializer(new StringRedisSerializer());

		// 设置hash key 和value序列化模式
		template.setHashKeySerializer(new StringRedisSerializer());
		template.setHashValueSerializer(jackson2JsonRedisSerializer);
		template.afterPropertiesSet();
	}

	private RedisCacheConfiguration defaultCacheConfig(Integer second) {
		Jackson2JsonRedisSerializer<Object> serializer =
				new Jackson2JsonRedisSerializer<>(Object.class);
		ObjectMapper mapper = new ObjectMapper();
		mapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);
		mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		serializer.setObjectMapper(mapper);

		RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
				.entryTtl(Duration.ofSeconds(second))
				.serializeKeysWith(RedisSerializationContext
						.SerializationPair
						.fromSerializer(new StringRedisSerializer()))
				.serializeValuesWith(
						RedisSerializationContext
								.SerializationPair
								.fromSerializer(serializer))
				.disableCachingNullValues();
		return config;
	}


}
